// Copyright © SixtyFPS GmbH <info@slint-ui.com>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial

// cSpell: ignore combobox spinbox standardbutton

import { LineEditInner, TextEdit, AboutSlint } from "../common/common.slint";
import { StandardButton } from "../common/standardbutton.slint";
import { StyleMetrics, ScrollView, Button, Palette  } from "std-widgets-impl.slint";
export { StyleMetrics, ScrollView, Button, StandardButton, TextEdit, AboutSlint }
export * from "widget-table-view.slint";

export component CheckBox {
    callback toggled;
    in property <string> text <=> text.text;
    in-out property <bool> checked;
    out property <bool> has-focus: fs.has-focus;
    in property<bool> enabled: true;
    min-height: 20px;
    horizontal-stretch: 0;
    vertical-stretch: 0;

    accessible-label <=> text.text;
    accessible-checkable: true;
    accessible-checked <=> root.checked;
    accessible-role: checkbox;

    HorizontalLayout {
        spacing: 8px;

        VerticalLayout {
            alignment: center;
            Rectangle {
                border-width: 1px;
                border-radius: 2px;
                /* border-color: !enabled ? Palette.neutralLighter : Palette.neutralSecondaryAlt;
                background: !enabled ? Palette.white
                    : touch.pressed ? Palette.neutralLight
                    : touch.has-hover ? Palette.neutralLighter
                    : Palette.themePrimary;*/

                border-color: root.checked ? self.background : !root.enabled ? Palette.neutralTertiaryAlt : Palette.neutralSecondaryAlt;
                background: !root.checked ? Palette.white
                            : !root.enabled ? Palette.neutralTertiaryAlt
                            : touch.has-hover || touch.pressed ? Palette.themeDark
                            : Palette.themePrimary;
                animate background { duration: 250ms; easing: ease; }

                //width: height;
                vertical-stretch: 0;
                width: 20px;
                height: 20px;

                if (root.checked || touch.has-hover || touch.pressed) : Path {
                    width: 66%;
                    height: 66%;
                    x: (parent.width - self.width) / 2;
                    y: (parent.height - self.height) / 2;
                    commands: "M.22.5.42.7.78.34.74.3.42.62.26.54z";
                    fill: root.checked ? Palette.white : Palette.neutralSecondaryAlt;
                }
            }
        }

        text := Text {
            color: !root.enabled ? Palette.neutralTertiary : Palette.neutralDark;
            horizontal-alignment: left;
            vertical-alignment: center;
            vertical-stretch: 1;
        }

    }

    touch := TouchArea {
        enabled <=> root.enabled;
        clicked => {
            if (root.enabled) {
                root.checked = !root.checked;
                root.toggled();
            }
        }
    }

    fs := FocusScope {
        x:0;
        width: 0px; // Do not react on clicks
        enabled <=> root.enabled;

        key-pressed(event) => {
            if (event.text == " " || event.text == "\n") {
                 touch.clicked();
                 return accept;
            }
            return reject;
        }
    }

    Rectangle { // Focus rectangle
        x: -3px;
        y: self.x;
        width: parent.width - 2*self.x;
        height: parent.height - 2*self.y;
        border-width: root.enabled && root.has-focus ? 1px : 0px;
        border-color: Palette.black;
    }
}

component SpinBoxButton inherits Rectangle {
    callback clicked <=> touch.clicked;
    in-out property<bool> enabled <=> touch.enabled;
    background: !root.enabled ? transparent
        : touch.pressed ? Palette.neutralLight
        : touch.has-hover ? Palette.neutralLighter
        : Palette.white;

    in-out property <color> symbol-color: !root.enabled ? Palette.neutralTertiary
        : touch.pressed || touch.has-hover ? Palette.neutralPrimary
        : Palette.neutralSecondary;
    touch := TouchArea { }
}

export component SpinBox {
    in-out property <int> value;
    in property <int> minimum;
    in property <int> maximum: 100;
    in property <bool> enabled <=> fs.enabled;
    out property <bool> has-focus <=> fs.has-focus;
    forward-focus: fs;

    min-height: max(32px, l.min-height);
    horizontal-stretch: 1;
    vertical-stretch: 0;

    accessible-role: spinbox;
    accessible-value: root.value;
    accessible-value-minimum: root.minimum;
    accessible-value-maximum: root.maximum;
    accessible-value-step: (root.maximum - root.minimum) / 100;

    fs := FocusScope {

        Rectangle {
            background: !root.enabled ? Palette.neutralLighter : Palette.white;
        }

        l := GridLayout {
            padding-left: 8px;
            padding-top: 3px;
            padding-bottom: 3px;
            text := Text {
                rowspan: 2;
                text: root.value;
                color: !root.enabled ? Palette.neutralTertiary : Palette.neutralDark;
                horizontal-alignment: left;
                vertical-alignment: center;
            }
            Rectangle { width: 8px; }
            button := SpinBoxButton {
                width: 25px;
                enabled: root.enabled;
                Path {
                    commands: "M978.2,688.9l-84.2,82.1c-15.7,15.3-41.1,15.3-56.7,0l-341-304.2L162.6,764.5c-15.5,15.1-41,15.1-56.6,0l-84.3-82.1c-15.6-15.2-15.6-39.9,0-55.2l446.6-398.2c15.7-15.3,41-15.3,56.7,0l6.9,6.7l446.3,398.1C993.9,649,993.9,673.7,978.2,688.9z";
                    fill: parent.symbol-color;
                    height: 33%;
                    x: (parent.width - self.width) / 2;
                    y: (parent.height - self.height) / 2;
                }
                clicked => {
                    if (root.value < root.maximum) {
                        root.value += 1;
                    }
            root.focus();
                }
            }
            SpinBoxButton {
                row: 1; col: 2;
                enabled: root.enabled;
                Path {
                    commands: "M21.8,311.1l84.2-82.1c15.7-15.2,41-15.2,56.7,0l341.1,304.1l333.7-297.5c15.5-15.2,41-15.2,56.6,0l84.3,82.1c15.6,15.2,15.6,40,0,55.2L531.7,771c-15.7,15.3-41,15.3-56.7,0l-6.9-6.7L21.8,366.3C6.1,351,6.1,326.3,21.8,311.1z";
                    fill: parent.symbol-color;
                    height: 33%;
                    x: (parent.width - self.width) / 2;
                    y: (parent.height - self.height) / 2;
                }
                clicked => {
                    if (root.value > root.minimum) {
                        root.value -= 1;
                    }
            root.focus();
                }
            }

        }

        Rectangle {
            x: root.enabled && root.has-focus ? -2px : 0px;
            y: self.x;
            width: parent.width - 2*self.x;
            height: parent.height - 2*self.y;
            border-radius: 2px;
            border-width: !root.enabled ? 0px : root.has-focus ? 3px : 1px;
            border-color: !root.enabled ? Palette.neutralLighter
                : root.has-focus ? Palette.themeSecondary
                : Palette.neutralDark;
        }

        key-pressed(event) => {
            if (root.enabled && event.text == Key.UpArrow && root.value < root.maximum) {
                root.value += 1;
                accept
            } else if (root.enabled && event.text == Key.DownArrow && root.value > root.minimum) {
                root.value -= 1;
                accept
            } else {
                reject
            }
        }
    }
}

export component Slider {
    in property<float> maximum: 100;
    in property<float> minimum: 0;
    in-out property<float> value;
    out property<bool> has-focus: fs.has-focus;
    in property<bool> enabled <=> touch.enabled;
    callback changed(float);

    min-height: 24px;
    min-width: 100px;
    horizontal-stretch: 1;
    vertical-stretch: 0;

    accessible-role: slider;
    accessible-value: root.value;
    accessible-value-minimum: root.minimum;
    accessible-value-maximum: root.maximum;
    accessible-value-step: (root.maximum - root.minimum) / 100;


    Rectangle {
        width: parent.width - parent.min-height;
        x: parent.height / 2;
        height: parent.min-height / 4;
        y: (parent.height - self.height) / 2;
        border-radius: self.height/2;
        background: !root.enabled ? Palette.neutralLighter
            : touch.has-hover ? Palette.themeLight
            : Palette.neutralTertiaryAlt;
    }

    Rectangle {
        width: (parent.width - parent.min-height) * ((root.value - root.minimum) / (root.maximum - root.minimum));
        x: parent.height / 2;
        height: parent.min-height / 4;
        y: (parent.height - self.height) / 2;
        border-radius: self.height/2;
        background: !root.enabled ? Palette.neutralTertiary
            : touch.has-hover ? Palette.themeSecondary
            : Palette.neutralSecondary;
    }

    handle := Rectangle {
        property<length> border: 3px;
        width: self.height;
        height: parent.height - 2 * self.border;
        border-width: 3px;
        border-radius: self.height / 2;
        border-color: !root.enabled ? Palette.neutralTertiaryAlt
            : touch.has-hover ? Palette.themePrimary
            : Palette.neutralSecondary;
        background: Palette.white;
        x: (root.width - handle.width) * (root.value - root.minimum)/(root.maximum - root.minimum);
        y: self.border;
    }
    touch := TouchArea {
        width: parent.width;
        height: parent.height;
        property <float> pressed-value;
        pointer-event(event) => {
            if (event.button == PointerEventButton.left && event.kind == PointerEventKind.down) {
                self.pressed-value = root.value;
            }
        }
        moved => {
            if (self.enabled && self.pressed) {
                root.value = max(root.minimum, min(root.maximum,
                    self.pressed-value + (touch.mouse-x - touch.pressed-x) * (root.maximum - root.minimum) / (root.width - handle.width)));
                root.changed(root.value);
            }
        }
    }

    fs := FocusScope {
        x:0;
        width: 0px;

        key-pressed(event) => {
            if (self.enabled && event.text == Key.RightArrow) {
                root.value = Math.min(root.value + 1, root.maximum);
                accept
            } else if (self.enabled && event.text == Key.LeftArrow) {
                root.value = Math.max(root.value - 1, root.minimum);
                accept
            } else {
                reject
            }
        }
    }

    Rectangle { // Focus rectangle
        border-width: root.enabled && root.has-focus ? 1px : 0px;
        border-color: Palette.black;
    }
}



export component GroupBox {
    in property <string> title <=> label.text;
    in property<bool> enabled: true;
    VerticalLayout {
        spacing: 8px;
        padding-top: 16px;
        padding-bottom: 8px;

        label := Text {
            vertical-stretch: 0;
            color: !root.enabled ? Palette.neutralTertiary : Palette.neutralDark;
            font-weight: 600;
        }
        Rectangle {
            vertical-stretch: 1;
            GridLayout {
                @children
            }
        }
    }
}

export component TabWidgetImpl  {
    out property <length> content-x: 0;
    out property <length> content-y: root.tabbar-preferred-height;
    out property <length> content-height: root.height - root.tabbar-preferred-height;
    out property <length> content-width: root.width;
    out property <length> tabbar-x: 0;
    out property <length> tabbar-y: 0;
    out property <length> tabbar-height: root.tabbar-preferred-height;
    out property <length> tabbar-width: root.width;

    in property <length> tabbar-preferred-height;
    in property <length> tabbar-preferred-width;
    in property <length> content-min-height;
    in property <length> content-min-width;
    in property <int> current-index;
    in property <int> current-focused;

    preferred-width: root.content-min-width;
    min-width: max(root.content-min-width, root.tabbar-preferred-width);
    preferred-height: root.content-min-height + root.tabbar-preferred-height;
    min-height: root.content-min-height + root.tabbar-preferred-height;
}

export component TabImpl inherits Rectangle {
    in property<string> title <=> t.text;
    in property<bool> enabled: true;
    property<bool> has-focus: root.current-focused == root.tab-index;
    in-out property<int> current; // The currently selected tab
    in property<int> current-focused; // The currently focused tab
    in property<int> tab-index; // The index of this tab
    in property<int> num-tabs; // The total number of tabs

    min-height: t.preferred-height + 16px;
    preferred-width: t.preferred-width + 16px;

    background: !root.enabled ? Palette.neutralLighter
        : touch.pressed ? Palette.neutralLight
        : touch.has-hover ? Palette.neutralLighter
        : Palette.white;
    horizontal-stretch: 0;
    vertical-stretch: 0;

    accessible-role: tab;
    accessible-label: root.title;

    touch := TouchArea {
        enabled <=> root.enabled;
        clicked => {
            root.current = root.tab-index;
        }
    }
    t := Text {
        width: parent.width;
        height: parent.height;
        vertical-alignment: center;
        horizontal-alignment: center;
        color: !root.enabled ? Palette.neutralTertiary : Palette.neutralPrimary;
        font-weight: root.current == root.tab-index ? 600 : 500;
    }

    Rectangle {
        height: 3px;
        width: touch.has-hover && root.current == root.tab-index ? parent.width : parent.width - 16px;
        animate width { duration: 250ms; easing: ease-out; }
        background: root.current == root.tab-index ? Palette.themeSecondary : transparent;
        y: parent.height - self.height;
        x: (parent.width - self.width) / 2;
    }

    Rectangle { // Focus Rectangle
        border-width: root.enabled && root.has-focus ? 1px : 0px;
        border-color: Palette.black;
    }
}

export component TabBarImpl {
    // injected properties:
    in-out property<int> current; // The currently selected tab
    in-out property<int> current-focused: fs.has-focus ? fs.focused-tab : -1; // The currently focused tab
    in-out property<int> num-tabs; // The total number of tabs

    HorizontalLayout {
        spacing: 8px;
        alignment: start;
        @children
    }

    accessible-role: tab;
    accessible-delegate-focus: root.current-focused >= 0 ? root.current-focused : root.current;

    fs := FocusScope {
        x:0;
        width: 0px; // Do not react on clicks
        property<int> focused-tab: 0;

        key-pressed(event) => {
            if (event.text == "\n") {
                 root.current = root.current-focused;
                 return accept;
            }
            if (event.text == Key.LeftArrow) {
                 self.focused-tab = Math.max(self.focused-tab - 1,  0);
                 return accept;
            }
            if (event.text == Key.RightArrow) {
                 self.focused-tab = Math.min(self.focused-tab + 1, root.num-tabs - 1);
                 return accept;
            }
            return reject;
        }

        key-released(event) => {
            if (event.text == " ") {
                 root.current = root.current-focused;
                 return accept;
            }
            return reject;
        }
    }
}

export component TabWidget inherits TabWidget {}

export component LineEdit {
    in property <length> font-size <=> inner.font-size;
    in-out property <string> text <=> inner.text;
    in property <string> placeholder-text <=> inner.placeholder-text;
    out property <bool> has-focus: inner.has-focus;
    in property <bool> enabled <=> inner.enabled;
    in property input-type <=> inner.input-type;
    in property horizontal-alignment <=> inner.horizontal-alignment;
    in property read-only <=> inner.read-only;
    callback accepted <=> inner.accepted;
    callback edited <=> inner.edited;
    forward-focus: inner;
  //  border-color: root.has-focus ? Palette.highlight-background : #ffffff;

    horizontal-stretch: 1;
    vertical-stretch: 0;
    min-height: max(32px, l.min-height);

    Rectangle {
        background: !root.enabled ? Palette.neutralLighter : Palette.white;
        border-radius: 2px;
        border-width: !root.enabled ? 0px : root.has-focus ? 2px : 1px;
        border-color: !root.enabled ? Palette.neutralLighter
            : root.has-focus ? Palette.themeSecondary
            : Palette.neutralPrimary;
    }

    l := HorizontalLayout {
        padding-left: 8px;
        padding-right: 8px;
        padding-top: 3px;
        padding-bottom: 3px;
        inner := LineEditInner {
            placeholder-color: !self.enabled ? Palette.neutralTertiary : Palette.neutralSecondary;
        }
    }
}

export component ListView inherits ScrollView {
    @children
}

component StandardListViewBase inherits ListView {
    private property <length> item-height: self.viewport-height / self.model.length;
    private property <length> current-item-y: self.viewport-y + current-item * item-height;

    in property<[StandardListViewItem]> model;
    in-out property<int> current-item: -1;

    for item[idx] in root.model : Rectangle {
        l := HorizontalLayout {
            padding: 8px;
            spacing: 0px;
            t := Text {
                text: item.text;
                color: Palette.neutralPrimary;
            }
        }
        background: idx == root.current-item ? Palette.neutralLighter
                    : touch.has-hover ? Palette.neutralLighterAlt : transparent;
        touch := TouchArea {
            width: parent.width;
            height: parent.height;

            clicked => { 
                set-current-item(idx);    
            }
        }
    }

    public function set-current-item(index: int) {
        if(index < 0 || index >= model.length) {
            return;
        }

        current-item = index;

        if(current-item-y < 0) {
            self.viewport-y += 0 - current-item-y;
        }
        
        if(current-item-y + item-height > self.visible-height) {
            self.viewport-y -= current-item-y + item-height - self.visible-height;
        }
    }
}

export component StandardListView inherits StandardListViewBase {
    FocusScope {
        key-pressed(event) => {
            if (event.text == Key.UpArrow) {
                root.set-current-item(root.current-item - 1);
                return accept;
            } else if (event.text == Key.DownArrow) {
                root.set-current-item(root.current-item + 1);
                return accept;
            }
            reject
        }
    }
}

export component ComboBox {
    in property <[string]> model;
    in-out property <int> current-index : 0;
    in-out property <string> current-value: root.model[root.current-index];
    //property <bool> is-open: false;
    callback selected(string);

    accessible-role: combobox;
    accessible-value <=> root.current-value;

    out property has-focus <=> fs.has-focus;
    in property enabled <=> fs.enabled;
    forward-focus: fs;

    fs := FocusScope {

        key-pressed(event) => {
            if (event.text == Key.UpArrow) {
                root.current-index = Math.max(root.current-index - 1, 0);
                root.current-value = root.model[root.current-index];
                return accept;
            } else if (event.text == Key.DownArrow) {
                root.current-index = Math.min(root.current-index + 1, root.model.length - 1);
                root.current-value = root.model[root.current-index];
                return accept;
            // PopupWindow can not get hidden again at this time, so do not allow to pop that up.
            // } else if (event.text == Key.Return) {
            //     touch.clicked()
            //     return accept;
            }
            return reject;
        }

        touch := TouchArea {
            enabled <=> root.enabled;
            clicked => {
                root.focus();
                popup.show();
            }
        }
    }

    Rectangle {
        background: !root.enabled ? Palette.neutralLighter : Palette.white;
        border-radius: 2px;
        border-width: !root.enabled ? 0px : root.has-focus ? 3px : 1px;
        border-color: !root.enabled ? Palette.neutralLighter
            : root.has-focus ? Palette.themeSecondary
            : Palette.neutralPrimary;
    }


    horizontal-stretch: 1;
    vertical-stretch: 0;
    min-width: 170px;
    min-height: max(32px, l.min-height);

    l := HorizontalLayout {
        padding-left: 8px;
        padding-right: 8px;
        padding-bottom: 3px;
        padding-top: 3px;
        spacing: 8px;
        t := Text {
            text <=> root.current-value;
            horizontal-alignment: left;
            vertical-alignment: center;
            horizontal-stretch: 1;
            color: !root.enabled ? Palette.neutralTertiary
                : root.has-focus || touch.has-hover ? Palette.neutralPrimary
                : Palette.neutralSecondary;
            min-width: 0;
        }
        Rectangle {
            width: 25px;
            Path {
                x: (parent.width - self.width) / 2;
                y: (parent.height - self.height) / 2;
                height: 8px;
                width: 25px;
                commands: "M.22.4.5.64.78.4.74.36.5.6.26.36z";
                fill: t.color;
            }
        }

    }

    popup := PopupWindow {
        x:0;
        y: root.height;
        width: root.width;
        Rectangle {
            border-color: Palette.neutralLighter;
            border-width: 1px;
            /*drop-shadow-color: Palette.neutralTertiary;
            drop-shadow-blur: 5px;*/
            background: Palette.white;
        }
        VerticalLayout {
            for value[idx] in root.model: Rectangle {
                background: idx == root.current-index ? Palette.neutralLighter
                    : item-area.has-hover ? Palette.neutralLighterAlt : transparent;
                VerticalLayout {
                    padding: 10px;
                    Text {
                        text: value;
                    }
                }
                item-area := TouchArea {
                    width: 100%;
                    height: 100%;
                    clicked => {
                        if (root.enabled) {
                            root.current-index = idx;
                            root.current-value = value;
                            root.selected(root.current-value);
                        }
                    }
                }
            }
        }
    }
}

export component VerticalBox inherits VerticalLayout {
    spacing: StyleMetrics.layout-spacing;
    padding: StyleMetrics.layout-padding;
}
export component HorizontalBox inherits HorizontalLayout {
    spacing: StyleMetrics.layout-spacing;
    padding: StyleMetrics.layout-padding;
}
export component GridBox inherits GridLayout {
    spacing: StyleMetrics.layout-spacing;
    padding: StyleMetrics.layout-padding;
}
